package com.cupdata.zicon.jdk7concurrent.chapter3;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;

/**
 * One of the most complex and powerful functionalities offered by the Java concurrency API is
the ability to execute concurrent-phased tasks using the Phaser class. This mechanism is
useful when we have some concurrent tasks divided into steps. The Phaser class provides us
with the mechanism to synchronize the threads at the end of each step, so no thread starts
its second step until all the threads have finished the first one.

As with other synchronization utilities, we have to initialize the Phaser class with the number
of tasks that participate in the synchronization operation, but we can dynamically modify this
number by increasing or decreasing it.

Once Phaser has been created, we launch three threads that execute three different
FileSearch objects.

JDK1.7 才支持

the Phaser class has two methods to increment the number of participants of a phaser.
These methods are as follows:
     register(): This method adds a new participant to Phaser. This new participant
will be considered as unarrived to the actual phase.
     bulkRegister(int Parties): This method adds the specified number of
participants to the phaser. These new participants will be considered as unarrived to
the actual phase.
    The only method provided by the Phaser class to decrement the number of participants is
the arriveAndDeregister() method that notifies the phaser that the thread has finished
the actual phase, and it doesn't want to continue with the phased operation.

 * 
 * @author SunYabing
 *
 */
public class PhaserTest {
	public static void main(String[] args) {
		Phaser phaser = new Phaser(3);
		                      //the number of tasks that participate in the synchronization operation
		                      //the number of threads that have to execute an arriveAndAwaitAdvance() method before
		                      //Phaser changes the phase and wakes up the threads that were sleeping.
		/**
		 * In this recipe, you will learn how to use the Phaser class to synchronize three concurrent
			tasks. The three tasks look for files with the extension .log modified in the last 24 hours in
			three different folders and their subfolders. This task is divided into three steps:
			1. Get a list of the files with the extension .log in the assigned folder and
				its subfolders.
			2. Filter the list created in the first step by deleting the files modified more than 24
				hours ago.
			3. Print the results in the console.
			
			At the end of the steps 1 and 2 we check if the list has any elements or not. If it hasn't any
			element, the thread ends its execution and is eliminated from the the phaser class.
		 */
		FileSearch system = new FileSearch("C:\\Windows", "log", phaser);
		FileSearch apps = new FileSearch("C:\\Program Files", "log", phaser);
		FileSearch documents = new FileSearch("C:\\Documents And Settings",
				"log", phaser);

		Thread systemThread = new Thread(system, "System");
		systemThread.start();

		Thread appsThread = new Thread(apps, "Apps");
		appsThread.start();

		Thread documentsThread = new Thread(documents, "Documents");
		documentsThread.start();

		try {
			systemThread.join();
			appsThread.join();
			documentsThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Terminated: "+ phaser.isTerminated());
	}
}

class FileSearch implements Runnable {

	private String initPath;
	private String end;
	private List<String> results;
	private Phaser phaser;

	public FileSearch(String initPath, String end, Phaser phaser) {
		this.initPath = initPath;
		this.end = end;
		this.phaser = phaser;
		results = new ArrayList<>();
	}

	private void directoryProcess(File file) {
		File list[] = file.listFiles();
		if (list != null) {
			for (int i = 0; i < list.length; i++) {
				if (list[i].isDirectory()) {
					directoryProcess(list[i]);
				} else {
					fileProcess(list[i]);
				}
			}
		}
	}

	private void fileProcess(File file) {
		if (file.getName().endsWith(end)) {
			results.add(file.getAbsolutePath());
		}
	}

	private void filterResults() {
		List<String> newResults = new ArrayList<>();
		long actualDate = new Date().getTime();
		for (int i = 0; i < results.size(); i++) {
			File file = new File(results.get(i));
			long fileDate = file.lastModified();
			if (actualDate - fileDate < TimeUnit.MILLISECONDS.convert(1,
					TimeUnit.DAYS)) {
				newResults.add(results.get(i));
			}
		}
		results = newResults;
	}

	private boolean checkResults() {
		if (results.isEmpty()) {
			System.out.printf("%s: Phase %d: 0 results.\n", Thread
					.currentThread().getName(), phaser.getPhase());
			System.out.printf("%s: Phase %d: End.\n", Thread.currentThread()
					.getName(), phaser.getPhase());
			/*
			 * notify Phaser object that this thread
			 *  has finished the actual phase, and it leaves the phased operation
			 */
			phaser.arriveAndDeregister();
			
			return false;
		} else {
			System.out.printf("%s: Phase %d: %d results.\n", Thread
					.currentThread().getName(), phaser.getPhase(), results
					.size());
			/*
			* notify Phaser object that this thread has finished the actual
			* phase and it wants to be blocked 
			* until all the participant threads in the phased
			* operation finish the actual phase.
			*/
			phaser.arriveAndAwaitAdvance();
			return true;
		}
	}

	private void showInfo() {
		System.out.println("list size:"+results.size());
		for (int i = 0; i < results.size(); i++) {
			File file = new File(results.get(i));
			System.out.printf("%s: %s\n", Thread.currentThread().getName(),
					file.getAbsolutePath());
		}
		phaser.arriveAndAwaitAdvance();
	}

	@Override
	public void run() {
		phaser.arriveAndAwaitAdvance();//The search won't begin until all the threads have been created.
		System.out.printf("%s: Starting.\n", Thread.currentThread().getName());
		File file = new File(initPath);
		if (file.isDirectory()) {
			directoryProcess(file);
		}

		if (!checkResults()) {
			return;
		}

		filterResults();
		if (!checkResults()) {
			return;
		}

		showInfo();
		phaser.arriveAndDeregister();
		System.out.printf("%s: Work completed.\n", Thread.currentThread()
				.getName());

	}

}
